'use strict';
var http=require('http');
var net=require('net'),path=require('path'),port=8080,mode='',version='1.1',exec=require('child_process').exec,upload=0,download=0;
var acea=process.argv.splice(2);
if(acea.length==1){
if(acea[0].match(/[0-9]\d*/)){var port=acea[0];}
else{mode=acea[0];}
}
else if(acea.length==2){
port=acea[0];
mode=acea[1];}

console.log();
console.log("CProxy 正在启动...");
createserver();
createadminserver();

function createserver(){
var server=net.createServer(
{allowHalfOpen:true},
function(client){client.on("end",function(){client.end();
client.destroy();
client=null;
return});
client.on("error",
function(err){client.end();
client.destroy();
client=null;
return});
client.on("timeout",
function(){client.end();
client.destroy();
client=null;
return});

var buffer=new Buffer(0);
client.on('data',
function(data){
buffer=buffer_add(buffer,data);
if(buffer_find_body(buffer) == -1) return;

var temp=buffer.toString();
if(mode!=""){if(temp.indexOf("benfw->"+mode+"-> ")==-1){client.destroy();
client=null;
return}}else{if(temp.indexOf("benfw-> ")==-1){client.destroy();
client=null;
return}}
var req=parse_request(buffer);
//LOG (req);
client.removeAllListeners('data');
client.removeAllListeners('end');
client.removeAllListeners('error');
client.removeAllListeners('timeout');
relay_connection(req)});


function relay_connection(req){
if(req==false){client.destroy();
return}
if(req.method!='CONNECT'){
var _2=buffer_find_body(buffer);
if(_2<0)_2=buffer.length;
var header=buffer.slice(0,_2).toString('utf8');
header=header.replace(/(Proxy-)?Connection\:.+\r\n/ig,'').replace(/Keep-Alive\:.+\r\n/i,'').replace("\r\n",'\r\nConnection: close\r\n').replace("Content-Type:application/vnd.wap.mms-message\r\n","").replace("Accept: */*, application/vnd.wap.mms-message, application/vnd.wap.sic\r\n","").replace(/X-Online-\:.+\r\n/ig,'').replace(/(X-Online-)?Host\:.+\r\n/ig,'').replace(/benfw\-\>(\w.+\-\>)?/,"Host:").replace("X-Forward-For: "+req.wzhost+"\r\n","").replace("X-Real-IP: "+req.wzhost+"\r\n","").replace("Referer: "+req.wzhost+"\r\n","").replace("\t","");
if(req.httpVersion=='1.1'){
var url=req.path.replace(/http\:\/\/[^\/]+/,'');
if(url.path!=url)header=header.replace(req.path,url)}buffer=buffer_add(new Buffer(header,'utf8'),buffer.slice(_2))}
else
{var header=buffer.toString('utf8');
header=header.replace("CONNECT "+req.wzhost,"CONNECT "+req.zshost+":"+req.zsport).replace("GET http://"+req.wzhost+"/->benfw","CONNECT "+req.zshost+":"+req.zsport).replace("Content-Type:application/vnd.wap.mms-message\r\n","").replace("Accept: */*, application/vnd.wap.mms-message, application/vnd.wap.sic\r\n","").replace(/X-Online-\:.+\r\n/ig,'').replace(/(X-Online-)?Host\:.+\r\n/ig,'').replace(/benfw\-\>(\w.+\-\>)?/,"Host:").replace("X-Forward-For: "+req.wzhost+"\r\n","").replace("X-Real-IP: "+req.wzhost+"\r\n","").replace("Referer: "+req.wzhost+"\r\n","").replace("\t","");
buffer=new Buffer(header,'utf8')}
if(req.zshost=="1.2.3.4"){req.zshost="127.0.0.1";
req.zsport=3000}
startService(client,{allowHalfOpen:true,port:req.zsport,host:req.zshost},{'connectfunc':proxyConnectAction,'req':req,'buffer':buffer});
buffer=null}}).on('error',
function(err){
if(err.code=='EADDRINUSE'){console.log();
LOG('CProxy 程序启动失败，'+port+' 端口被占用');
process.exit()}}).listen(port,

function(){console.log('');
console.log('=========================================================================');
console.log('');
console.log('                            欢迎使用NodeJs版CProxy                       ');
console.log('');
console.log('                     奔放免流网:http://benfw.cn                ');
console.log('=========================================================================');
console.log('');
console.log('当前版本：v'+version);
console.log('GET/POST方法伪装:\n[MTD] [uri] HTTP/1.1\nbenfw->'+(mode==''?' ':mode+'-> ')+'[host]\nHost: 伪装host');
console.log('CONNECT方法伪装:\nCONNECT 伪装host\nbenfw->'+(mode==''?' ':mode+'-> ')+'真[host]\nHost: 伪装host\n');
console.log('CProxy 正在监听 '+port+' 端口');
console.log('');
console.log('免流配置模板请去奔放网查看');
console.log('')})}

function proxyConnectAction(client,server,req,buffer){
if(req.method=='CONNECT'){client.write(new Buffer('HTTP/1.1 200 Connection established\r\n\r\n'));
}else{upload+=buffer.length;
server.write(buffer)}};

function startService(client,options,connopts){
var server=net.createConnection(options);
client.pause();
server.pause();
server.on('connect',function(socket){client.resume();
server.resume();
connopts.connectfunc(client,server,connopts.req,connopts.buffer)});
client.on('data',function(data){upload+=data.length;
});
server.on('data',function(data){download+=data.length});
client.on('end',function(){server.end()});
server.on('end',function(){});
client.on('error',function(){server.destroy()});
server.on('error',function(){client.end();
client.destroy()});
client.on('timeout',function(){server.destroy();
client.destroy()});
server.on('timeout',function(){server.destroy();
client.destroy()});
client.pipe(server);
server.pipe(client)}

function createadminserver(){
var libHttp=require('http');
var libHttps=require('https');
var libUrl=require('url');
var libFs=require('fs');
var libPath=require('path');
var funGetContentType=function(filePath){var contentType='';
var ext=libPath.extname(filePath);
switch(ext){
case'.html':contentType='text/html';
break;
case'.js':contentType='text/javascript';
break;
case'.css':contentType='text/css';
break;
case'.gif':contentType='image/gif';
break;
case'.jpg':contentType='image/jpeg';
break;
case'.png':contentType='image/png';
break;
case'.ico':contentType='image/icon';
break;
case"jpeg":contentType="image/jpeg";
break;
case"jpg":contentType="image/jpeg";
break;
case"js":contentType="text/javascript";
break;
case"json":contentType="application/json";
break;
case"pdf":contentType="application/pdf";
break;
case"png":contentType="image/png";
break;
case"svg":contentType="image/svg+xml";
break;
case"swf":contentType="application/x-shockwave-flash";
break;
case"tiff":contentType="image/tiff";
break;
case"txt":contentType="text/plain";
break;
case"wav":contentType="audio/x-wav";
break;
case"wma":contentType="audio/x-ms-wma";
break;
case"wmv":contentType="video/x-ms-wmv";
break;
case"xml":contentType="text/xml";
break;
default:contentType='application/octet-stream'}
return contentType;
};
var funWebSvr=function(req,res){
var reqUrl=req.url;
reqUrl=reqUrl.replace(/http\:\/\/[^\/]+/,'');
switch(reqUrl){case'/?queryFlow':var uploadFlow=((upload/ 1024 ) /1024).toFixed(2).toString()+' MB';
var downloadFlow=((download/ 1024 ) /1024).toFixed(2).toString()+' MB';
var obj={'upload':uploadFlow,'download':downloadFlow,};
obj=JSON.stringify(obj);
res.write(obj);
res.end();
break;
case'/?memory':var obj={'memory':(process.memoryUsage().rss/ 1024 /1024).toFixed(2).toString()+' MB'};
obj=JSON.stringify(obj);
res.write(obj);
res.end();
break;
case'/?version':var obj={'version':version};
obj=JSON.stringify(obj);
res.write(obj);
res.end();
break;
case'/?clear':gc();
res.end();
break;
default:var pathName=libUrl.parse(reqUrl).pathname;
if(libPath.extname(pathName)==''){pathName+='/';
};
if(pathName.charAt(pathName.length-1)=='/'){pathName+='index.html';
};
var filePath=libPath.join('./cpadmin',pathName);
res.writeHead(200,{'Content-Type':funGetContentType(filePath)});
var stream=libFs.createReadStream(filePath,{flags:'r',encoding:null});
stream.on('error',function(){res.writeHead(404);
res.end('<h1>404错误</h1>')});
stream.pipe(res);
stream.on('end',function(){stream.unpipe(res);
res.end()})}};
var webSvr=libHttp.createServer(funWebSvr);
webSvr.on('error',function(error){if(error.code=='EADDRINUSE'){
console.log('');
LOG('CProxy 后台启动失败，3000 端口被占用');
process.exit()}});
webSvr.listen(3000,function(){});
process.on('uncaughtException',function(err){});}

function CurentTime(){
var now=new Date();
var year=now.getFullYear();
var month=now.getMonth()+1;
var day=now.getDate();
var hh=now.getHours();
var mm=now.getMinutes();
var ss=now.getSeconds();
var clock=year+'-';
if(month<10)clock+='0';
clock+=month+'-';
if(day<10)clock+='0';
clock+=day+' ';
if(hh<10)clock+='0';
clock+=hh+':';
if(mm<10)clock+='0';
clock+=mm+':';
if(ss<10)clock+='0';
clock+=ss;
return(clock)}

function LOG(str){console.log(CurentTime()+'\t'+str)}

function buffer_add(buf1,buf2){
var re=new Buffer(buf1.length+buf2.length);
buf1.copy(re);
buf2.copy(re,buf1.length);
return re};

function buffer_find_body(b){
for(var i=0,len=b.length-3;
i<len;
i++){if(b[i]==0x0d&&b[i+1]==0x0a&&b[i+2]==0x0d&&b[i+3]==0x0a){return i+4}}return-1}

function parse_request(buffer){
var s=buffer.toString('utf8');
var firstLine=s.split('\n')[0];
var method;
LOG(buffer);

if(firstLine.indexOf('->benfw')!=-1){method='CONNECT'}
else{
	var match=firstLine.match(/([A-Z]+)\s/);
if(!match||!match[1]){return false}method=match[1]}
if(method=='CONNECT'){
	var arr=s.match(/^([A-Z]+)\s([^\s]+)\sHTTP\/(\d.\d)/);
if(arr&&arr[1]&&arr[2]&&arr[3])var wzhost=s.match(/Host\:\s+([^\n\s\r]+)/);
var zshost=s.match(/benfw\-\>(\w.+\-\>)?\s+([^\n\s\r]+)/);
if(zshost&&wzhost&&zshost[2]&&wzhost[1]){
	var _0=wzhost[1].replace('\t','').split(':',2);
var _1=zshost[2].replace('\t','').split(':',2);
LOG("M:CONNECT,假H:"+(_0[0])+",假P:"+(_0[1]?_0[1]:80)+",真H:"+(_1[0])+",真P:"+(_1[1]?_1[1]:443)+",V:"+(arr[3]));
return{method:'CONNECT',wzhost:_0[0],wzport:_0[1]?_0[1]:80,zshost:_1[0],zsport:_1[1]?_1[1]:443,httpVersion:arr[3]}}}
else{var arr=s.match(/^([A-Z]+)\s([^\s]+)\sHTTP\/(\d.\d)/);
if(arr&&arr[1]&&arr[2]&&arr[3]){var wzhost=s.match(/Host\:\s+([^\n\s\r]+)/);
var zshost=s.match(/benfw\-\>(\w.+\-\>)?\s+([^\n\s\r]+)/);
if(wzhost&&zshost&&zshost[2]&&wzhost[1]){var _0=wzhost[1].replace('\t','').split(':',2);
var _1=zshost[2].replace('\t','').split(':',2);
LOG("M:"+(arr[1])+",假H:"+(_0[0])+",假P:"+(_0[1]?_0[1]:80)+",真H:"+(_1[0])+",真P:"+(_1[1]?_1[1]:80)+",U:"+(arr[2])+",V:"+(arr[3]));
return{method:arr[1],wzhost:_0[0],wzport:_0[1]?_0[1]:80,zshost:_1[0],zsport:_1[1]?_1[1]:80,path:arr[2],httpVersion:arr[3]}
}}}

return false}
